package org.rcy.framework.data.id;

import tk.mybatis.mapper.genid.GenId;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author 吴康桥
 * @Description
 * @date 2022/9/27 18:18
 */


public class LongIdGenerator implements GenId<Long> {
	private static final long epoch = 30 * 365 * 24 * 3600000L; // 时间纪元 2000-01-01 00:00 00
	private static final long workerIdBits = 5L; // 节点ID长度
	private static final long datacenterIdBits = 5L; // 数据中心ID长度
	private static final long sequenceBits = 12L; // 序列号12位
	private static final long workerIdShift = sequenceBits; // 机器节点左移12位
	private static final long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心节点左移17位
	private static final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间毫秒数左移22位
	private static final long sequenceMask = -1L ^ (-1L << sequenceBits); // 4095
	private static final ThreadLocal<DateFormat> start = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd 00:00:00.0"));
	private static final ThreadLocal<DateFormat> end = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd 23:59:59.999"));
	private static final ThreadLocal<DateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));

	/**
	 * 调用Spring初始化的IdGenerator
	 */
	private static IdGenerator<Long> idGeneratorInstance;

	@Override
	public Long genId(String table, String column) {
		return generate();
	}

	public static Long generate() {
		return idGeneratorInstance.generate();
	}

	protected static void setIdGeneratorInstance(IdGenerator<Long> idGeneratorInstance) {
		LongIdGenerator.idGeneratorInstance = idGeneratorInstance;
	}

	public static Long maxId(Date date) {
		if (date == null) {
			return 0L;
		}
		String format = end.get().format(date);
		try {
			Date parse = dateFormat.get().parse(format);
			long timestamp = parse.getTime();
			return ((timestamp - epoch) << timestampLeftShift) | (31 << datacenterIdShift) | (31 << workerIdShift) | (4095 & sequenceMask);
		} catch (ParseException e) {
			return 0L;
		}
	}

	public static Long minId(Date date) {
		if (date == null) {
			return 0L;
		}
		String format = start.get().format(date);
		try {
			Date parse = dateFormat.get().parse(format);
			long timestamp = parse.getTime();
			return ((timestamp - epoch) << timestampLeftShift);
		} catch (ParseException e) {
			return 0L;
		}
	}

	public static Date idToTime(Long id) {
		if (id == null) {
			return new Date();
		}
		Long timestamp = (id >> timestampLeftShift) + epoch;
		return new Date(timestamp);
	}

	public static long getDatacenterId(Long id) {
		if (id == null) {
			return -1L;
		}
		return (31L << datacenterIdShift & id) >> datacenterIdShift;
	}

	public static long getWorkerId(Long id) {
		if (id == null) {
			return -1L;
		}

		return (31L << sequenceBits & id) >> sequenceBits;
	}

	public static Long changeId(Long id, Date date) {
		if (id == null || date == null) {
			return 0L;
		}
		long dateTime = date.getTime();
		// 对时间戳毫秒位增加随机数（不突破当前秒，防止秒位变动导致整个日期跨天跨月），毫秒位：mod，随机数范围：-mod ~ (1000 - mod)
		long mod = dateTime % 1000;
		ThreadLocalRandom tlr = ThreadLocalRandom.current();
		long nextLong = tlr.nextLong(-mod, 1000 - mod);
		return (dateTime + nextLong - epoch) << timestampLeftShift | (((31 << datacenterIdShift) | (31 << workerIdShift) | (4095 & sequenceMask)) & id);
	}
}
